// Level.cpp -- Level Class Implementation for reading Lemmings levels (*.lvl)
// Copyright (C) 2007, 2008  Brett McLarnon, Morgan Conbere
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
// USA.
//
// $Id: Level.cpp 18 2008-12-26 20:33:33Z mconbere $
//

#include <iostream>

#include "Level.h"
#include "../util/Util.h"


namespace level
{

static const int cReadMax = 64;
static const int cMaxObjects = 32;
static const int cMaxTerrain = 400;
static const int cMaxSteel = 32;

Level::Level(std::istream &in)
{
    char buffer[cReadMax];

    in.read(buffer, 8);
    mReleaseRate = util::Swap(((uint16_t*)buffer)[0]);
    mLemmingCount = util::Swap(((uint16_t*)buffer)[1]);
    mLemmingRescue = util::Swap(((uint16_t*)buffer)[2]);
    mTimeLimit = util::Swap(((uint16_t*)buffer)[3]);

    in.read(buffer, 2 * cNumSkills);
    mSkills[Climber] = util::Swap(((uint16_t*)buffer)[0]);
    mSkills[Floater] = util::Swap(((uint16_t*)buffer)[1]);
    mSkills[Bomber]  = util::Swap(((uint16_t*)buffer)[2]);
    mSkills[Blocker] = util::Swap(((uint16_t*)buffer)[3]);
    mSkills[Builder] = util::Swap(((uint16_t*)buffer)[4]);
    mSkills[Basher]  = util::Swap(((uint16_t*)buffer)[5]);
    mSkills[Miner]   = util::Swap(((uint16_t*)buffer)[6]);
    mSkills[Digger]  = util::Swap(((uint16_t*)buffer)[7]);

    in.read(buffer, 8);
    mScreenStart = util::Swap(((uint16_t*)buffer)[0]);
    mTileset = (Tileset)util::Swap(((uint16_t*)buffer)[1]);
    // ((uint16_t*)buffer)[2] not used in windows, defines extened graphics set
    // ((uint16_t*)buffer)[3] not used, a valid value is 0x0000.

    mLevelWidth = 0;

    for(int i = 0; i < cMaxObjects; ++i)
    {
        in.read(buffer, 8);

        Object object;
        object.x = util::Swap(((int16_t*)buffer)[0]) - 16;
        object.y = util::Swap(((int16_t*)buffer)[1]);
        object.id = util::Swap(((uint16_t*)buffer)[2]);

        uint16_t modifier = util::Swap(((uint16_t*)buffer)[3]);
        object.modifier = None;
        object.modifier |= (modifier & 0x008F) ? Flip : None;
        object.modifier |= (modifier & 0x4000) ? OnlyTerrain : None;
        object.modifier |= (modifier & 0x8000) ? NoOverwrite : None;
        
        if(((uint64_t*)buffer)[0] != 0)
        {
            mObjects.push_back(object);
        }
    }

    for(int i = 0; i < cMaxTerrain; ++i)
    {
        in.read(buffer, 4);

        Terrain terrain;
        uint32_t modifier = buffer[0] >> 4;
        terrain.modifier = None;
        terrain.modifier |= (modifier & 0x2) ? Remove : None;
        terrain.modifier |= (modifier & 0x4) ? Flip : None;
        terrain.modifier |= (modifier & 0x8) ? NoOverwrite : None;  

        terrain.x = (util::Swap(((int16_t*)buffer)[0]) & 0x0fff) - 16;
        terrain.y = (util::Swap(((int16_t*)buffer)[1]) >> 7) - 4;
        terrain.id = buffer[3] & 0x3f;

        if(((uint32_t*)buffer)[0] != (uint32_t)-1)
        {
            mTerrain.push_back(terrain);
        }
    }

    for(int i = 0; i < cMaxSteel; ++i)
    {
        in.read(buffer, 4);

        Steel steel;
        steel.x = ((util::Swap(((int16_t*)buffer)[0]) & 0xff80) >> 5) - 16;
        steel.y = (buffer[2] & 0x3f) * 4;
        steel.height = (buffer[3] & 0x0f) * 4 + 4;
        steel.width = (buffer[3] >> 4) * 4 + 4;

        if(((uint32_t*)buffer)[0] != 0)
        {
            mSteel.push_back(steel);
        }
    }

    in.read(buffer, 32);
    for(int i = 31; i >= 0; ++i)
    {
        if(buffer[i] != ' ')
        {
            break;
        }
        buffer[i] = 0;
    }

    char *name = buffer;
    for(int i = 0; i < 32; ++i)
    {
        if(buffer[i] != ' ')
        {
            name = buffer + i;
            break;
        }
    }

    mName = name;
}

void Level::print(std::ostream &out)
{
    out << "Level: " << mName << std::endl;
    out << "  Release rate: " << mReleaseRate << std::endl;
    out << "  Lemming Count: " << mLemmingCount << std::endl;
    out << "  Lemming Rescue: " << mLemmingRescue << std::endl;
    out << "  Time limit: " << mTimeLimit << std::endl;
    out << "  Screen start: " << mScreenStart << std::endl;
    out << "  Tileset: " << mTileset << std::endl;

    // Objects
    out << "  Objects (" << mObjects.size() <<"):" << std::endl;
    for (unsigned int i = 0; i < mObjects.size(); ++i) {
        out << "    (" << mObjects[i].x
            << ", " << mObjects[i].y
            << ") " << mObjects[i].id
            << " [" << mObjects[i].modifier
            << "]" << std::endl;
    }

    // Terrain 
    out << "  Terrain (" << mTerrain.size() << "):" << std::endl;
    for (unsigned int i = 0; i < mTerrain.size(); ++i) {
        out << "    (" << mTerrain[i].x
        << ", " << mTerrain[i].y
        << ") " << (int)mTerrain[i].id
        << " [" << mTerrain[i].modifier
        << "]" << std::endl;
    }

    // Steel
    out << "  Steel (" << mSteel.size() << "):" << std::endl;
    for (unsigned int i = 0; i < mSteel.size(); ++i) {
        out << "    (" << mSteel[i].x
        << ", " << mSteel[i].y
        << ") " << mSteel[i].height
        << "x" << mSteel[i].width
        << std::endl;
    }
}

} // namespace level
